#!/usr/bin/env python
# -*- coding: utf-8 -*-
import env
import argparse
import serial
import re, os, sys
import datetime
import time
import random
import numpy as np
import threading
import matplotlib.pyplot as plt

current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_dir)
from lib_sds import LibSDS

# -------------------------------
getts = lambda fmt: datetime.datetime.now().strftime(fmt)
tslog = lambda: getts("%Y/%m/%d %H:%M:%S.%f")
tsday = lambda: getts("%Y%m%d")
tsname = lambda: getts("%Y%m%d%H%M%S")
dataPath = lambda mlbsn: '/vault/LVCCAC/Data3/{}'.format(mlbsn)
# dataPath = lambda mlbsn: '/Users/judith/Desktop/aProject/Oscilloscope/Siglent/Log/vault/LVCCAC/Data3/{}'.format(
#     mlbsn)
sdsLock = threading.RLock()
ChannelNames = [str(i) for i in range(1, 9)]

# Define a lock for self.data.mvData
mvData_lock = threading.Lock()

TBFULL_FLOAT = [100E-12,200E-12, 500E-12, 1E-9, 2E-09,
                5E-9, 10E-9, 20E-9,
                50E-9, 100E-9, 200E-9, 500E-9, 1E-6, 2E-6, 5E-6, 10E-6, 20E-6, 50E-6, 100E-6, 200E-6,
                500E-6, 1E-3, 2E-3,5E-3, 10E-3, 20E-3,
                50E-3, 100E-3, 200E-3, 500E-3, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000]

def getRangeIdxByUserName(name):
    try:
        _rName = name.upper()
        return VoltageRange.index(_rName)
    except Exception as e:
        raise e


def getRangeInfoByUserIdx(idx):
    try:
        return VoltageRange[int(idx)]
    except Exception as e:
        raise e

# -------------------------------
# fixture uart port list
FixtureUart = [
    "/dev/cu.usbserial-MLB10",
    "/dev/cu.usbserial-MLB11",
    "/dev/cu.usbserial-MLB12",
    "/dev/cu.usbserial-MLB13"
]


# idx in 1..4
def getUartNameByIdx(idx):
    _idx = int(idx) - 1
    try:
        return FixtureUart[_idx]
    except Exception as e:
        raise e


# -------------------------------
def getLogFullPath(mlbsn, ts, siglentsn, test):
    psn = siglentsn.replace('/', '')
    logName = 'retdata_{}_{}_{}_{}.log'.format(mlbsn, ts, psn, test)
    return os.path.join(dataPath(mlbsn), logName)


def getImgFullPath(mlbsn, ts, siglentsn, test, lcnt=None):
    lcntInfo = '' if not lcnt else '_Loop{}'.format(lcnt)
    psn = siglentsn.replace('/', '')
    pngName = '{}_{}_{}_{}{}.png'.format(mlbsn, ts, psn, test, lcntInfo)
    return os.path.join(dataPath(mlbsn), pngName)


# -------------------------------


class AttrDict(dict):
    def __getattr__(self, key):
        try:
            return self[key]
        except Exception as e:
            raise e

    def __setattr__(self, key, value):
        self[key] = value


def getRandomNum():
    while True:
        t = random.random()
        if 0.1 < t < 0.35:
            return round(t * 10, 2)


# -------------------------------


def _wloginfo(info, end='\n'):
    _msg = tslog() + '\t' + info + end
    print(_msg.strip())
    return _msg


# -------------------------------
class SiglentData(object):
    def __init__(self, siglentSn):
        self.sn = siglentSn
        self.status = AttrDict()

        self.triggerStatus = None
        self.timebase = None #1E-12#100ps #5E-4  # 500us

        self.waveform = 0
        self.mvData = []
        self.probe = 1

        self.triggerPercent = 12 # 20% trigger delay
        self.nSamples = 10E6

        self.timeout = None
        self.isTimeout = 0

        self.triggerLevel = None
        self.PMUVcpuVDD = None
        self.memory_depth = None

        self.resolution = PCfgKey.KResolution  # 8B {8Bits|10Bits}
        self.coupling = PCfgKey.KCoupling
        self.actual_timebase_interval = None
        self.impedence = PCfgKey.KImpedence #"ONEMeg","FIFTy"
        self.loopCount = 0
        self.dutcmd = None
        self.usbtimeout = None

    def _allocDefaultDataBuffer(self):
        self.dataBuffer = []
        self.mvData = []
        for chName in self.onChNames:
            self.mvData.append(None)

    def setUserOnChs(self, chIdxStr):
        # chIdxStr = 1,2,3 ....
        self.onChNames = chIdxStr.split(',')
        self._allocDefaultDataBuffer()


    def _setUserRangeByName(self, rangeStr):
        self.onChRangeInfo = rangeStr.split(',')
        self.onChRanges = [getRangeIdxByUserName(r) for r in self.onChRangeInfo]

    def _setUserRangeByNum(self, numStr):
        # numStr = 6,6,8 ...
        self.onChRanges = [int(i) for i in numStr.split(',')]
        if len(self.onChRanges) != len(self.onChNames):
            raise Exception('channels config count != Range config count')
        self.onChRangeInfo = [getRangeInfoByUserIdx(i) for i in self.onChRanges]

    def setUserOnChRanges(self, inputStr):
        if re.match(r"^[\d,]+$", inputStr):
            self._setUserRangeByNum(inputStr)
        else:
            self._setUserRangeByName(inputStr)

    def setUserOnChOffsets(self, offsetStr):
        # offsetStr = a,b,c ....
        self.onChOffsets = [float(offset) for offset in offsetStr.split(',')]  # V
        self.onChOffsetsMV = [float(offset)*1000 for offset in offsetStr.split(',')] # mV
        if len(self.onChRanges) != len(self.onChOffsets):
            raise Exception('channels config count != Offset config count')

    def setUserTriggerLevel(self, value):
        self.triggerLevel = int(value)  # mV

    def setUserPmuVcpuVDD(self,value):
        self.PMUVcpuVDD = float(value) # uV
        offsetStr = []
        for i in range(len(self.onChNames)):
            offsetStr.append(str(self.PMUVcpuVDD/1e6*(-1))) if i==0 else offsetStr.append('0')
        self.setUserOnChOffsets(','.join(offsetStr))
        # self._calcTriggerAdcCount()

# -------------------------------
class SiglentController(object):
    def __init__(self, params, log, uartport, usbport):
        # sdsscope parameters
        self.data = params
        # instance created by class
        self.log = log
        self.uart = uartport
        self.usbport = usbport

    def _printData(self, chn, tc, key, value):
        self.log.write("Channel:" + chn + " TestCount:" + str(tc) + " " + key + ":" + repr(round(value, 3)) + 'mV')

    def _setChannelOn(self):
        # idx : 1..4, '1,2'
        self.log.write('Start Run sds6108lSetChannelOn')
        self.log.write('Coupling:' + PCfgKey.KCoupling)
        self.log.write('Bandwidth:' + PCfgKey.KBandwidth)
        try:
            for chName in ChannelNames:
                if chName == '':
                    continue
                if chName in self.data.onChNames:
                    _chRange = self.data.onChRangeInfo[self.data.onChNames.index(chName)]
                    _chOffset = self.data.onChOffsets[self.data.onChNames.index(chName)]
                    _cpl = self.data.coupling
                    _probe = self.data.probe
                    self.usbport.ConfigureChannel("C"+chName, _chRange, _chOffset, _cpl, _probe, "ON")
                    self.usbport.set_ch_impedance("C"+chName, self.data.impedence)
                else:
                    # switch channel off
                    self.usbport.set_ch_trace_status("C"+chName, 'OFF')

        except Exception as e:
            self.log.writeError("sds6108lSetChannelOn Error: " + str(e))
            raise e

    def _setSimpleTrigger(self):
        ''' one SDS device, set first channel trigger '''
        self.log.write('Start Run sds6108l SetSimpleTrigger')
        self.log.write('Set Trigger On Channel:' + self.data.onChNames[0])
        self.log.write('Set Trigger Voltage:{}mV'.format(self.data.triggerLevel))
        try:
            chName = self.data.onChNames[0]
            channelName = 'C{}'.format(chName)
            self.usbport.set_trig_type("EDGE",channelName)
            # _chOffset = self.data.onChOffsets[self.data.onChNames.index(chName)]
            _triggerLevel = self.data.PMUVcpuVDD/1e6 - self.data.triggerLevel/1e3
            self.usbport.set_trig_level(_triggerLevel, channelName, "EDGE")
            self.usbport.set_trigger_slope("ALTernate", 'EDGE') #RISing
            self.usbport.set_trig_single()
            triggerMode = self.usbport.get_trig_mode()
            if 'SINGLE' not in triggerMode:
                self.log.write('[ERROR] triggerMode <{}>is not SIGNLE'.format(triggerMode))

            # screenShotFile = self.data.pngfile.replace('.png', '_screen.png')
            # self.usbport.screen_shot("PNG", screenShotFile)

        except Exception as e:
            self.log.writeError("sds6108lSetSimpleTrigger Error: " + str(e))
            raise e

    def _getTimebase(self):
        ''' device property '''
        self.log.write('Start Run sds6108lGetTimebase')
        try:
            self.usbport.set_acq_mem_mgmt('AUTO')
            self.usbport.set_trig_auto()
            self.usbport.set_memory_depth(self.data.memory_depth)
            timebase = self.data.timebase
            self.usbport.set_timebase(timebase)
            triggerPercent = self.data.triggerPercent
            if triggerPercent > 50:
                triggerDelayTime = timebase*10*(triggerPercent/100.0) - timebase*5
            else:
                triggerDelayTime = -(timebase*5 - timebase*10*(triggerPercent/100.0))
            self.usbport.set_timebase_delay(triggerDelayTime)

        except Exception as e:
            self.log.writeError('sds6108lGetTimebase Error: ' + str(e))
            raise e

    def _waitSignetTriggerReady(self):
        self.log.write('Start Run sds6108lIsReady??')
        self.data.startTime = time.time()
        # time.sleep(0.2)
        self.log.write('waitting for ready ....')
        isReady = self.usbport.wait_for_acquire_complete(self.data.timeout)
        if not isReady:
            self.log.writeError("sds6108lIsReady Error: " + 'timeout, userTimeout={}s'.format(self.data.timeout))
            self.data.isTimeout = 1
        self.log.write('Finish sds6108lIsReady')

    def async_data_conversion(self, recv_all, wf_param, index):
        self.log.write('[Thread {}]begain convert data'.format(index))
        convert_data = np.frombuffer(recv_all, dtype=np.uint8).reshape(-1, 2)
        data = convert_data[:, 1] * 256 + convert_data[:, 0]
        data = data.astype(np.int32)
        bit = wf_param["adc_bit"]
        mask = pow(2, bit - 1) - 1
        maxValue = pow(2, bit) - 1
        data = np.where(data > mask, data - maxValue, data)
        self.log.write('[Thread {}]begin convert data to mv'.format(index))
        volt_value = data / wf_param["code"] * float(wf_param["vdiv"]) - float(wf_param["offset"])
        volt_value = np.array(volt_value) * 1000
        with mvData_lock:
            if self.data.mvData[index] is None:
                self.data.mvData[index] = [volt_value]
            else:
                self.data.mvData[index].append(volt_value)

    def _getDataAfterTrigger(self, chName):
        self.log.write('Start Run sds6108lGetValues')
        channelName = 'C'+ chName
        self.usbport.write("WAV:POINT {}".format(self.data.nSamples))

        wf_param = self.usbport.get_wvfm_preamble(channelName)
        self.data.actual_timebase_interval = wf_param["interval"]
        self.data.nSamples = wf_param["point"]
        wf_param["tdiv"] = TBFULL_FLOAT[wf_param["tdiv"]]
        print(wf_param)
        recv_all = self.usbport.get_wvfm_real_data_16bit(channelName)
        self.log.write('wf_param is {}'.format(wf_param))
        self.log.write('recv_all len is {}'.format(len(recv_all)))
        return recv_all, wf_param

    def run_getDataAfterTrigger(self, chName, i):
        recv_all, wf_param = self._getDataAfterTrigger(chName)
        self.async_data_conversion(recv_all, wf_param, i)


    def closeUnit(self):
        self.log.write('Start Run sds6108lCloseUnit')
        try:
            self.usbport.remove()
        except Exception as e:
            self.log.writeError('sds6108lCloseUnit Error: ' + str(e))
            raise e

    def run(self):
        test_count = 0
        while test_count < self.data.loopCount:
            test_count += 1
            self.log.write("Test count:{}".format(test_count))
            self.log.write(self.usbport.get_ip_address())
            self._setChannelOn()
            self._getTimebase()
            self._setSimpleTrigger()
            #Delay to fix sc run 19 380 Trigger None issue
            time.sleep(2)
            self.uart.sendAndRead(self.data.dutcmd)
            self._waitSignetTriggerReady()
            # screenShotFile = self.data.pngfile.replace('.png', '_screen1.png')
            # self.usbport.screen_shot("PNG", screenShotFile)
            if self.data.isTimeout == 0:
                threads = []
                for i in range(len(self.data.onChNames)):
                    chName = self.data.onChNames[i]
                    recv_all, wf_param = self._getDataAfterTrigger(chName)
                    # Execute async_data_conversion in separate threads for each channel
                    thread = threading.Thread(target=self.async_data_conversion, args=(recv_all, wf_param, i))
                    threads.append(thread)
                    thread.start()

                # Wait for all threads to finish
                for thread in threads:
                    thread.join()

                for i in range(len(self.data.onChNames)):
                    chName = self.data.onChNames[i]
                    wfData = self.data.mvData[i][-1]
                    self._printData(chName, test_count, 'Vmean_output', wfData.mean())
                    self._printData(chName, test_count, 'Vmin_output', wfData.min())
                    self._printData(chName, test_count, 'Vmax_output', wfData.max())
                    self._printData(chName, test_count, 'Vdroop_output', self.data.PMUVcpuVDD / 1000.0 - wfData.min())
            self.data.isTimeout = 0


# -------------------------------

def getMinMinMatchDataFor1ps2ch():
    global psdata,log
    _matchData = []
    minIndex = -1
    vminValue = 1e9
    for i in range(len(psdata.mvData[0])):
        _cwfData = psdata.mvData[0][i]  # sds channel C, Positive
        _dwfData = psdata.mvData[1][i]  # sds channel D, Negative
        _cdmatch = _cwfData - _dwfData
        _matchData.append(_cdmatch)
        log.write('{}:psdata.mvData[0][{}][0] = {}, psdata.mvData[1][{}][0] = {},_matchData = {}'.format(i,i,_cwfData[0],i,_dwfData[0], _matchData[0][0]))
        _vmin = _cdmatch.min()
        if _vmin < vminValue:
            vminValue = _vmin
            minIndex = i
        log.write('minIndex = {}'.format(minIndex))
    return _matchData, minIndex


def buildFigure_three_col():
    def _getTextInfo(tData):
        _info = 'Vmax:{:.3f}mV\n'.format(tData.max())
        _info += 'Vmin:{:.3f}mV\n'.format(tData.min())
        _info += 'Vmean:{:.3f}mV\n'.format(tData.mean())
        _info += 'Vstd:{:.3f}mV\n'.format(tData.std())
        return _info

    # ---------------get data---------------
    global psdata, log
    log.write('start to get data psdata.nSamples = {}, actual_timebase_interval = {}'.format(psdata.nSamples,psdata.actual_timebase_interval))
    time = np.linspace(0, (psdata.nSamples - 1) * psdata.actual_timebase_interval * 1e3, psdata.nSamples)
    matchDataList, minIdx = getMinMinMatchDataFor1ps2ch()
    positiveData = psdata.mvData[0][minIdx]
    negativeData = psdata.mvData[1][minIdx]
    matchData = matchDataList[minIdx]
    log.write('positive')

    # ---------------positive---------------
    plt.figure(dpi=80, figsize=(12.8, 6.4))
    plt.rcParams["font.size"] = 8
    # plt.subplot(411)
    plt.subplot2grid((4, 10), (0, 0), colspan=9)
    points = psdata.nSamples
    # plt.plot(time, positiveData[:], c='blue', label='Positive')
    plt.plot(time[:points], positiveData[:points], c='blue', label='Positive')
    plt.ylabel('Voltage (mV)')
    plt.xticks([])
    xmax, ymin = plt.xlim()[1], plt.ylim()[0]
    pInfo = 'Positive\n' + _getTextInfo(positiveData)
    pInfo += 'Offset:{:.3f}V\n'.format(psdata.onChOffsets[0])
    plt.text(xmax + 0.02, ymin, pInfo, fontsize=9, color='blue')

    # ---------------negative---------------
    plt.subplot2grid((4, 10), (1, 0), colspan=9)
    plt.plot(time[:points], negativeData[:points], c='green', label='Negative')
    plt.ylabel('Voltage (mV)')
    plt.xticks([])
    xmax, ymin = plt.xlim()[1], plt.ylim()[0]
    nInfo = 'Negative\n' + _getTextInfo(negativeData)
    nInfo += 'Offset:{:.3f}V\n'.format(psdata.onChOffsets[1])
    plt.text(xmax + 0.02, ymin, nInfo, fontsize=9, **{'color': 'green'})

    # --------------match----------------
    plt.subplot2grid((4, 10), (2, 0), colspan=9)
    plt.plot(time[:points], matchData[:points], c='red', label='Match')
    plt.xlabel('Time (ms)')
    plt.ylabel('Voltage (mV)')
    xmax, ymin = plt.xlim()[1], plt.ylim()[0]
    log.write('SDS_Match_Vmax:{}mV'.format(matchData.max()))
    log.write('SDS_Match_Vmin:{}mV'.format(matchData.min()))
    log.write('SDS_Match_Vmean:{}mV'.format(matchData.mean()))
    log.write('SDS_Match_Vstd:{}mV'.format(matchData.std()))
    plt.text(xmax + 0.02, ymin, 'Match\n' + _getTextInfo(matchData), fontsize=9, **{'color': 'red'})

    # -------------sds config info-----------------
    sdsInfo = 'triggerLevel:{:.3f}mV\n'.format(psdata.triggerLevel)
    sdsInfo += '{}\n'.format('Resolution:' + PCfgKey.KResolution)
    sdsInfo += '{}\n'.format('timebase:{}us/div'.format(psdata.timebase*1e6))
    sdsInfo += '{}\n'.format('preTriggerSample:{}Mpts'.format(psdata.nSamples*psdata.triggerPercent/100/1e6))
    plt.subplot2grid((4, 10), (3, 0), colspan=3)
    plt.axis('off')
    xmin, ymin = plt.xlim()[0], plt.ylim()[0]
    plt.text(xmin, ymin, sdsInfo, fontsize=9)

    # ----------
    sdsInfo = 'PMU:{:.3f}uV\n'.format(psdata.PMUVcpuVDD)
    if PCfgKey.KBandwidth == 'FULL':
        sdsInfo += '{}\n'.format('Bandwidth:' + "1GHZ")
    else:
        sdsInfo += '{}\n'.format('Bandwidth:' + "20MHz")
    # sdsInfo += '{}\n'.format('sampleInterval:{:.3f}ns'.format(psdata.actual_timebase_interval*1e9))
    sdsInfo += '{}\n'.format('Impedence:{} ohm'.format(PCfgKey.KImpedence))
    sdsInfo += '{}\n'.format('postTriggerSample:{}Mpts'.format(psdata.nSamples*(100-psdata.triggerPercent)/100/1e6))
    plt.subplot2grid((4, 10), (3, 3), colspan=3)
    plt.axis('off')
    xmin, ymin = plt.xlim()[0], plt.ylim()[0]
    plt.text(xmin, ymin, sdsInfo, fontsize=9)
    # ----------
    sdsInfo = '{}\n'.format('Coupling:' + PCfgKey.KCoupling)
    sdsInfo += '{}\n'.format('sampleRate:{:.0f}MSa/s'.format(1/psdata.actual_timebase_interval/1e6))
    sdsInfo += '{}\n'.format('totalSample:{}Mpts'.format(psdata.nSamples/1e6))
    plt.subplot2grid((4, 10), (3, 6), colspan=3)
    plt.axis('off')
    xmin, ymin = plt.xlim()[0], plt.ylim()[0]
    plt.text(xmin, ymin, sdsInfo, fontsize=9)

    # ------------------------------
    plt.rcParams['agg.path.chunksize'] = 5000
    plt.savefig(psdata.pngfile, dpi=80)

# plt.show()

# -------------------------------
class UartController(object):
    def __init__(self, port, log, br, timeout):
        # 
        if re.match(r'^\d$', port):
            self.portname = getUartNameByIdx(port)
        else:
            self.portname = port
        self.UART = serial.Serial(self.portname, baudrate=br, timeout=0.2)
        self.log = log
        self.usbtimeout = timeout
        self.log.write('SerialPort:' + str(self.portname))
        self.log.write('Debug usbtimeout:' + str(timeout))

    def sendAndRead(self, cmd, delimiter='SEGPE>'):
        self.log.write('Start Run UART sendAndRead \"{}\"'.format(cmd))
        try:
            # 1. check open status
            if self.UART.isOpen():
                self.UART.close()
            # 2. to open uart
            if not self.UART.isOpen():
                self.log.write('to open uart: ' + self.portname)
                self.UART.open()
                self.log.write('uart is open')
            # 3. send command
            cmd_str = cmd + "\n"
            self.UART.write(cmd_str.encode('utf-8'))
            self.log.write(delimiter + str(cmd))
            # 4. read response
            self.UART.flushOutput()
            #response = self.UART.readall()
            time_end = time.time() + self.usbtimeout
            self.log.write("debug usbtimeout1:" + str(self.usbtimeout))
            while True:
                response = self.UART.readline().decode('utf-8')
                if response:
                    self.log.write(delimiter + str(response))
                if "PASS " + cmd in response:
                    break
                if time.time() > time_end:
                    break

            #self.log.write(delimiter + str(response))
            # 5. close uart
            if self.UART.isOpen():
                self.log.write('to close uart {}'.format(self.portname))
                self.UART.close()
                self.log.write('uart is closed')
        except Exception as e:
            self.log.writeError("UART {}, WRITE CMD {}, Error: {}".format(self.portname, cmd, str(e)))
            raise e

class LogController(object):
    def __init__(self, logfile):
        logpath = os.path.dirname(logfile)
        if not os.path.exists(logpath):
            print(os.system('mkdir -p ' + logpath))
        self.logfile = logfile
        _wloginfo('LogFile:' + self.logfile, end='')

    def write(self, info):
        with open(self.logfile, 'a') as fd:
            _info = info.replace('\r', '')
            while '\n' in _info:
                idx = _info.index('\n')
                line, _info = _info[:idx], _info[idx + 1:]
                fd.write(_wloginfo(line))
            fd.write(_wloginfo(_info))
            fd.flush()

    def writeError(self, einfo=None):
        with open(self.logfile, 'a') as fd:
            if einfo:
                fd.write(_wloginfo(einfo))
            fd.write(_wloginfo("Vmean_output: -1 mv"))
            fd.write(_wloginfo("Vmin_output: -1 mV"))
            fd.write(_wloginfo("Vmax_output: -v mV"))
            fd.write(_wloginfo("PMU_output: -v mV"))

def mainFor1ps2ch(TestValues=None):
    # 1ps 2chs, 1mlb 2TestNodes, 1cmd, 1uartport
    # ------------------------------
    if not TestValues:
        args = parseArguments()
    elif isinstance(TestValues, AttrDict):
        args = TestValues
    # ------------------------------
    mlbsn = str(args.dutsn)  # 1value: 'C07GUQ0006Y00003PN'
    usb_address = args.usbaddress # 1value: USB0::0xF4EC::0x1013::SDS6LA3X7R0057::INSTR
    siglentsn = str.split(usb_address,"::")[3]#str(args.siglentsn)  # 1value: 'SDS6LA2D6R0026'
    testname = str(args.testname)  # 1value: 'GFX-PERF-MG00A-SC31'
    # systs = str(args.systime)  # 1value: '20230518183729'
    # ------------------------------
    timestamp = tsname()
    logfile = getLogFullPath(mlbsn, timestamp, siglentsn, testname)
    global psdata, log
    log = LogController(logfile)
    log.write('PYTHON VERSION > {}'.format(SDS_PYTHON_VERSION))
    _ret = re.match(r'Namespace\((.*)\)', str(args))
    if _ret:
        log.write(_ret.group(1))
    # ------------------------------
    pngfile = getImgFullPath(mlbsn, timestamp, siglentsn, testname)
    # ------------------------------
    # isDefTP = args.triggerlevel and args.pmuvcpu
    # if not isDefTP and not args.adccnt:
    # 	raise Exception("Get Trigger ADC Data Error")
    psdata = SiglentData(siglentsn)
    psdata.setUserOnChs(args.chindex)  # 2value: '1,2'
    psdata.setUserOnChRanges(args.chrange)  # 2value: '6,8'
    # psdata.setUserOnChOffsets(args.choffset)  # 2value: '0,10'
    psdata.setUserTriggerLevel(args.triggerlevel)  # 1value: 25
    psdata.setUserPmuVcpuVDD(args.pmuvcpu) # 1value: '885000.000000'
    psdata.timeout = float(args.timeout)  # 1value: 8[s]
    psdata.timebase = float(args.timebase)
    psdata.memory_depth = args.memorydepth
    psdata.dutcmd = str(args.dutcmd)  # 1value: "pdump run_list -l 1"
    psdata.usbtimeout = float(args.usbtimeout)  # 1value: 8[s]
    if args.triggerpercent:
        psdata.triggerPercent = float(args.triggerpercent)

    psdata.pngfile = pngfile
    if args.loopcnt:
        psdata.loopCount = int(args.loopcnt)  # 1value: 3
    else:
        psdata.loopCount = 1
    # ------------------------------
    if args.uartport:
        uart = UartController(args.uartport, log,230400,psdata.usbtimeout)  # 1value: idx in 1..4 or '/dev/cu.usbserial-MLB1A'
    else:
        # default with the first sds chindex
        uart = UartController(args.chindex.split(',')[0], log,230400,psdata.usbtimeout)
    # ------------------------------
    try:
        log.write('start create LibSDS')
        usbport = LibSDS(args.usbaddress, log)
        log.write('start set usbport timeout')
        usbport.set_timeout(300000)
        log.write('start create SiglentController')
        ps = SiglentController(psdata, log, uart, usbport)
        try:
            log.write('start to run')
            ps.run()
        except Exception as e:
            log.write(str(e))
            ps.closeUnit()
        ps.closeUnit()
    except Exception as e:
        log.write('{}, create LibSDS or SiglentController error'.format(str(e)))

    try:
        buildFigure_three_col()
    except Exception as e:
        log.write(str(e))
    log.write('buildFigure_over!!')


def parseArguments():
    parser = argparse.ArgumentParser(description='Control PicoScope to get test data of the DUT')
    # ------------------------------
    parser.add_argument('--dutsn', type=str, required=True, help='dutsn like \'C07GVF000AC00003PG\'')
    parser.add_argument('--testname', type=str, required=True, help='testname like \'VDD_GFX-PERF-MG00A-SC31\'')
    parser.add_argument('--dutcmd', type=str, required=True, help='dutcmd like \'sc run 19\'')
    # ------------------------------
    parser.add_argument('--uartport', type=str, required=False, help='uart port name for each sds device')
    # ------------------------------
    parser.add_argument('--chindex', type=str, required=True, help='channels like \'1\' or \'1,2\'')
    parser.add_argument('--chrange', type=str, required=True, help='channel range')
    # parser.add_argument('--choffset', type=str, required=True, help='value of sds channel offset')
    parser.add_argument('--pmuvcpu', type=str, required=False, help='value of PMU_Vcpu_VDD')
    # ------------------------------
    parser.add_argument('--loopcnt', type=str, required=False, help='dut command loop count')
    parser.add_argument('--systime', type=str, required=False, help='system timestamp like \'20230403174559\'')
    parser.add_argument('--timeout', type=str, required=True, help='timeout of read sds data to waitting')
    parser.add_argument('--adccnt', type=str, required=False, help='sds trigger voltage by adc count')
    parser.add_argument('--triggerlevel', type=str, required=False, help='trigger level')
    parser.add_argument('--usbaddress', type=str, required=True, help='usb address')
    parser.add_argument('--timebase', type=str, required=True, help='timebase second unit like 2E-4 => 200us')
    parser.add_argument('--memorydepth', type=str, required=True, help='memory depth like 5M/12.5M')
    parser.add_argument('--usbtimeout', type=str, required=True, help='usb timeout of read sds data to waitting')
    parser.add_argument('--triggerpercent', type=str, required=False, help='usb timeout of read sds data to waitting')
    return parser.parse_args()

# Siglent config key
PCfgKey = AttrDict()
PCfgKey.KResolution = '10B'  # 'SDS_DR_10BIT'
PCfgKey.KBandwidth = 'FULL'
PCfgKey.KCoupling = 'DC'
PCfgKey.KImpedence = 'ONEMeg' #"ONEMeg","FIFTy"

VoltageRange = [
    "0.01", "0.02", "0.05", "0.1", "0.2", "0.5",
    "1", "2", "5", "10", "20", "50", "100", "200"
] #V

SDS_PYTHON_VERSION = 'sds6108L_2023_0807_v{}.2'.format(LibSDS.__version__)

if __name__ == '__main__':
    # test command
    test_params = AttrDict({
        # ---------------
        # 'chindex': '1,2',
        # 'chrange': '6,6',
        # 'choffset': '-0.915,0',
        # ---------------
        'chindex':'4,8',
        'chrange':'0,0',   #See VoltageRange table, start from 0 index
        # 'choffset':'-1.5,0', #V
        # ---------------
        # 'chindex':'2',
        # 'chrange':'7',
        # 'choffset':'0',
        # ---------------
        'siglentsn': 'SDS6LA3X7R0057',
        'dutsn': 'C07GUY0005800003PT',
        'testname': 'VDD_GFX-PERF-MG00A-SC31',
        'triggerlevel': '20', #mV
        'pmuvcpu': '830000.000000',  #uV
        # 'systime': '20230725182123',
        'dutcmd': 'sc run 19', #'pdump run_list -l 1', 100us/mem depth 5M/pmuvcpu 900 ; sc run 19 200us/mem depth 12.5M/pmuvcpu 830
        'loopcnt': '1',
        'timeout': '8',
        'uartport': None,
        'usbaddress': 'USB0::0xF4EC::0x1013::SDS6LA3X7R0057::INSTR',
        'timebase': 2E-4,
        'memorydepth': '12.5M',
        'usbtimeout': '10'
    })
    #mainFor1ps2ch(test_params)
mainFor1ps2ch()
